﻿using System;
using System.Transactions;
using System.Web;
using System.Web.UI;

namespace Shumisen.Web.Form.Modules
{
	// TODO Pageの属性を見る
	public class TransactionModule : IHttpModule
	{
		public void Init(HttpApplication context)
		{
			context.PreRequestHandlerExecute += OnPreRequestHandlerExecute;
			context.EndRequest += OnEndRequest;
		}

		public void Dispose()
		{
		}

		protected virtual void OnPreRequestHandlerExecute(Object sender, EventArgs e)
		{
			var context = HttpContext.Current;
			var page = context.Handler as Page;
			if (page == null)
			{
				return;
			}
			page.PreInit += OnPreInit;
			page.PreRenderComplete += OnPreRenderComplete;
			page.Unload += OnUnload;
			page.Error += OnError;
		}

		protected virtual void OnEndRequest(Object sender, EventArgs e)
		{
			var tx = Transaction.Current;
			if (tx == null)
			{
				return;
			}
			Gurad.AssertTrue(tx.TransactionInformation.Status != TransactionStatus.Active);
		}

		protected virtual void OnPreInit(Object sender, EventArgs e)
		{
			// TODO Pageの属性チェック
			// TODO 設定ファイルによるデフォルトのTransactionOptionsの設定
			BeginTransaction(TransactionScopeOption.Required,
			                 new TransactionOptions
			                 {
			                 	IsolationLevel = IsolationLevel.ReadCommitted,
								Timeout = TransactionManager.DefaultTimeout
			                 });
		}

		protected virtual void OnPreRenderComplete(Object sender, EventArgs e)
		{
			CommitTransaction();
		}

		protected virtual void OnUnload(Object sender, EventArgs e)
		{
			CommitTransaction();
		}

		protected virtual void OnError(Object sender, EventArgs e)
		{
			var context = HttpContext.Current;
			RollbackTransaction(context.Error);
		}

		protected virtual void BeginTransaction(TransactionScopeOption txScopeOption, TransactionOptions txOptions)
		{
			var context = HttpContext.Current;
			context.Items[HttpContextItemNames.TransactionScope] = new TransactionScope(txScopeOption, txOptions);
			// TODO ログ出力
		}

		protected virtual void CommitTransaction()
		{
			EndTransaction((txScope, tx) =>
			               {
			               	if (tx.TransactionInformation.Status == TransactionStatus.Active)
			               	{
			               		txScope.Complete();
			               		// TODO ログ出力
			               	}
			               });
		}

		protected virtual void RollbackTransaction(Exception exception)
		{
			EndTransaction((txScope, tx) =>
			               {
			               	if (tx.TransactionInformation.Status == TransactionStatus.Active)
			               	{
			               		if (exception == null)
			               		{
			               			tx.Rollback();
			               		}
			               		else
			               		{
			               			tx.Rollback(exception);
			               		}
			               		// TODO ログ出力
			               	}
			               });
		}

		protected virtual void EndTransaction(Action<TransactionScope, Transaction> action)
		{
			var context = HttpContext.Current;
			var txScope = context.Items[HttpContextItemNames.TransactionScope] as TransactionScope;
			context.Items[HttpContextItemNames.TransactionScope] = null;
			if (txScope == null)
			{
				return;
			}
			using (txScope)
			{
				var tx = Transaction.Current;
				if (tx == null)
				{
					return;
				}
				action(txScope, tx);
			}
		}
	}
}